home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Original Shareware 1.1
/
The Original Shareware (WeMake CDs)(Volume 1.1)(CDs, Inc)(1993).iso
/
11
/
trs002.zip
/
TSRSH.ASM
next >
Wrap
Assembly Source File
|
1987-02-01
|
26KB
|
1,088 lines
PAGE 60,132
TITLE Ram-Resident Program Shell, Preliminary Version (Lesson 2)
SUBTTL Date 02-01-87 -- Tutorial for Microsoft Forum
;-----------------------------------------------------------------------------
; Ram-Resident Program Shell
;
;
;The software, documentation and source code are:
;
; (C) Copyright 1986, 1987
; Chip Rabinowitz
; All Rights Reserved
;
; 51 East Rogues Path
; Huntington Station NY 11746
; Assistant SysOp, Microsoft Forum, Compuserve PPN 76703,350
;
;
;COPYRIGHT NOTICE AND WARRANTY INFORMATION
;-----------------------------------------
;
;This document ("the source code"), other accompanying written and disk-based
;notes and specifications ("the documentation"), and all referenced and related
;program files accompanying the source code and the documentation ("the
;software") are copyrighted by Chip Rabinowitz.
;
;This program has been uploaded to Compuserve as part of a Tutorial in how
;to write Terminate-But-Stay-Resident programs, being conducted on
;Compuserve's Microsoft Special Interest Group during early 1987/
;
;This code (and all other code uploaded by the Copyright Holder as a
;part of this TSR Tutorial) may be used with the following restrictions:
;
;(1) If all or part of this code is used as part of a software package of
;ANY kind, the following acknowledgement must be used in the Documentation
;accompanying said package:
;
; 'Parts of the Resident Program Management for this Product are'
; 'Copyright (C) 1986, 1987 by Chip Rabinowitz, and are incorporated'
; 'into this product courtesy of the Ringmaster Development Team'
;
;(2) If all or part of this code is used as part of a software package that
;is placed in the public domain, no further restrictions apply.
;
;(3) If all or part of this code is used as part of a software package that
;is being distributed as 'shareware', a one-time-only donation of $10 will be
;accepted for the express purpose of continuing research into TSR standards.
;Note that this is a voluntary contribution, and should be sent to the
;Ringmaster Development Team at the address printed above.
;
;(4) If all or part of this code is used as part of a software package that
;will be distributed as a commercial product, a one-time-only payment of
;$25 will be accepted the express purpose of continuing research into TSR
;standards. Note that in THIS CASE ONLY, this is not a voluntary
;contribution. Commercial Developers are REQUIRED to make payment prior to
;incorporating these routines into their product.
;
;No copy of the source code may be distributed or given away without the
;accompanying documentation; and this notice must not be removed.
;
;There is no warranty of any kind associated with this software, and the
;copyright owner is not liable for damages of any kind. By using this
;software, you agree to this.
;
;
XON equ 11h
XOFF equ 13h
TRUE equ 1
FALSE equ 0
CR equ 0dh
LF equ 0ah
TAB equ 9
HOTKEYON equ 01h ;hot key pressed
SHIFTSON equ 02h ;shift states match
TSRACTIVE equ 04h ;tsr is running in foreground
INT28ACTIVE equ 08h ;INT28 routine is running in background
KB_DATA equ 60h
KB_CTL equ 61h
KB_FLG1 equ 17h
KB_FLG2 equ 18h
BIOS_DATA equ 40h
code segment para public 'CODE'
org 100h
assume cs:code, ds:code, es:code
start: jmp begin
db 1bh,'[2J'
cpyrt db 'Ram-Resident Program Shell, Preliminary Version (Lesson 2)',
db CR,LF,LF
db 'Copyright (C) 1986, 1987 Chip Rabinowitz',
db CR,LF,'All Rights Reserved',CR,LF,LF,'$'
hello db 'Hello World!',CR,LF,'$'
bad_dos_msg db 'Incompatible DOS version .... Aborting!',07h,CR,LF,'$'
tsr_parms:
PSP dw ? ;PSP of TSR
OLDPSP dw ? ;location to save current PSP
DTA dd ? ;DTA of TSR
OLDDTA dd ? ;location to save current DTA
DSEG dw ? ;TSR's Data Segment
SSEG dw ? ;TSR's Stack Segment ....
SPTR dw 0fffeh ;.... and Stack Pointer
ISSEG dw 0 ;interrupted SS ...
ISPTR dw 0 ;..... and SP
SHIFTST db 0 ;Shift State to Activate
HOTKEY db 0ffh ;scan code to activate
STATUS dw 0 ;current status words
POPUP dd 0 ;far pointer to popup routine
intflags db 0
ININT13 equ 04h ;Bios disk interrupt
ININT21 equ 08h ;DOS interrupt
dosversion db 0 ;current version of DOS
waitcount db 0 ;wait to activate count
indosflag dd 0 ;Pointer to INDOS flag
doscriterr dd 0 ;Pointer to DOS Critical Error Flag
dossseg dw 0 ;pointer to dos stack segment
dossptr dw 0 ;pointer to dos stack pointer
dossize dw 0 ;dos stack size
dos21funcs label byte
db 0, 0, 0, 0, 0, 0, 0, 0 ;0-7
db 0, 0, 0, 0, 0, 0ffh, 0ffh, 0ffh ;8-f
db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ;10-17
db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ;18-1f
db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0, 0ffh ;20-27
db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0 ;28-2f
db 0ffh, 0, 0, 0, 0, 0, 0ffh, 0ffh ;30-37
db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ;38-3f
db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ;40-47
db 0, 0, 0, 0, 0ffh, 0, 0ffh, 0ffh ;48-4f **
db 0, 0, 0, 0, 0ffh, 0, 0ffh, 0ffh ;50-57
db 0, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0, 0 ;58-5f
db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ;60-67
;
; ***************************************************************************
; These are the bios interrupt vectors
;
BIOSI8 equ 08h ;Bios Timer interrupt
BIOSI9 equ 09h ;BIOS Hardware Keyboard interrupt
BIOSIB equ 0Bh
BIOSIC equ 0Ch
BIOSI10 equ 10h
BIOSI13 equ 13h ;Bios Disk interrupt
BIOSI14 equ 14h
BIOSI15 equ 15h
BIOSI16 equ 16h ;Bios Keyboard interrupt
BIOSI17 equ 17h
BIOSI1C equ 1Ch
DOSI21 equ 21h ;DOS service router interrupt
DOSI28 equ 28h ;DOS Idle interrupt
DOSI33 equ 33h ;mouse interrupt
BIOSI1B equ 1Bh
DOSI23 equ 23h
DOSI24 equ 24h
;
oldint8 dd 0 ;BIOS Hardware Timer Interrupt
db BIOSI8
dw offset newint8 ;replacement vector
oldint9 dd 0 ;BIOS Hardware Keyboard Interrupt
db BIOSI9
dw offset newint9 ;replacement vector
oldint13 dd 0 ;BIOS Disk Interrupt
db BIOSI13
dw offset newint13 ;replacement vector
oldint16 dd 0 ;BIOS Software Keyboard Interrupt
db BIOSI16
dw offset newint16 ;replacement vector
oldint21 dd 0 ;DOS Services Interrupt
db DOSI21
dw offset newint21 ;replacement vector
oldint28 dd 0 ;DOS Idle Interrupt
db DOSI28
dw offset newint28 ;replacement vector
oldint1B dd 0 ;control-C vector
db BIOSI1B
dw offset intret ;replacement vector
oldint23 dd 0 ;control-C vector
db DOSI23
dw offset intret ;replacement vector
oldint24 dd 0 ;critical error vector
db DOSI24
dw offset newint24 ;replacement vector
;
; ***************************************************************************
; Function to restore interrupts.
;
; If AX=0, this function does popup-interrupts (1B, 23 and 24)
; If AX=anything else, this function does initial interrupts
;
restore_int proc near
push ds
push ax
push dx
push si
push di
push cx
mov di,7 ;7 bytes per structure
or ax,ax
jnz restore1
mov cx,3 ;three times through
mov si,offset oldint1B ;start at table beginning
jmp restore_loop
restore1:
mov cx,06h ;six interrupts!
mov si,offset oldint8 ;top of table
restore_loop:
push cx
push di ;save structure size
push ds
mov al,4[si] ;interrupt number
mov dx,[si] ;IP of interrupt vector
mov ds,2[si] ;CS of interrupt vector
mov ah,25h ;set interrupt vector function
int 21h
pop ds
xor ax,ax ;clear this entry from table
mov [si],ax ;so it shows not used
mov 2[si],ax
pop di ;get size back
add si,di ;do it again
pop cx
loop restore_loop
pop cx
pop di
pop si
pop dx
pop ax
pop ds
ret
restore_int endp
;
; ***************************************************************************
; Function to set interrupts.
;
; If AX=0, this function does popup-interrupts (1B, 23 and 24)
; If AX=anything else, this function does initial interrupts
;
setup_int proc near
push es
push ax
push bx
push dx
push di
push si
push cx
mov di,7 ;7 bytes per structure
or ax,ax
jnz setup1
mov cx,3 ;three times through
mov si,offset oldint1B ;start at table beginning
jmp setup_loop
setup1:
mov cx,06h ;six interrupts!
mov si,offset oldint8 ;top of table
setup_loop:
push cx
push di ;save structure size
; push ds
mov ax,[si] ;check for value
or ax,ax ;if not zero, next one
jnz setup2
mov al,4[si] ;interrupt number
mov ah,35h ;get interrupt vector function
int 21h
; pop ds
mov [si],bx ;save IP
mov 2[si],es ;save CS
mov al,4[si] ;interrupt number
;DS already contains correct segment
mov dx,5[si] ;IP of interrupt vector
mov ah,25h ;set interrupt vector function
int 21h
setup2:
pop di ;get size back
add si,di ;do it again
pop cx
loop setup_loop
ex_set:
pop cx
pop si
pop di
pop dx
pop bx
pop ax
pop es
ret
setup_int endp
setdta macro ddptr
push ds
mov ax,1a00h ;Function used to get current DTA address }
mov dx,word ptr ddptr ;Offset of DTA returned }
mov ds,word ptr ddptr+2 ;Segment of DTA returned by DOS }
int 21h ;Execute MSDos function request }
pop ds
endm
getdta macro ddptr
push es
mov ax,2f00h ;Function used to get current DTA address }
int 21h ;Execute MSDos function request }
mov word ptr ddptr+2,es ;Segment of DTA returned by DOS }
mov word ptr ddptr,bx ;Offset of DTA returned }
pop es
endm
;
; ***************************************************************************
; S E T P S P
;
; A bug in DOS 2.0, 2.1, causes DOS to clobber its standard stack }
; Then the PSP get/set functions are issued at the DOS prompt. The }
; following checks are made, forcing DOS to use the "critical" }
; stack when the TSR enters at the INDOS level. }
;
; BX contains the PSP segment to set
;
setpsp proc near
call checkindos ;If Version < 3, and INDOS,...
jz do_set_psp
mov byte ptr es:[di],0ffh ;set critical flag
do_set_psp:
mov ax,5000h ;set PSP* function
;BX already holds segment
int 21h ;DOS function request
call checkindos ;If Version < 3, and INDOS,...
jz set_out
mov byte ptr es:[di],0h ;set critical flag
set_out:
ret
setpsp endp
;
; ***************************************************************************
; G E T P S P
;
; A bug in DOS 2.0, 2.1, causes DOS to clobber its standard stack }
; Then the PSP get/set functions are issued at the DOS prompt. The }
; following checks are made, forcing DOS to use the "critical" }
; stack when the TSR enters at the INDOS level. }
;
; The PSP Segment is returned in BX
;
getpsp proc near
call checkindos ;If Version < 3, and INDOS,...
jz do_get_psp
mov byte ptr es:[di],0ffh ;set critical flag
do_get_psp:
mov ax,5100h ;get PSP function
int 21h ;DOS function request
;BX now holds PSP segment
call checkindos ;If Version < 3, and INDOS,...
jz get_out
mov byte ptr es:[di],0h ;clear critical flag
get_out:
ret
getpsp endp
;
; ***************************************************************************
; This routine checks the version of DOS in use, and returns the
; ZF=0 (Not zero) if version < 3.0, and INDOS is set. Internal function
; is called ONLY by getpsp and setpsp.
;
checkindos proc near
mov al,dosversion
cmp al,3 ;If Version is less than 3.0 ...
jge ok_ret
mov es,word ptr indosflag+2 ; ... and ...
mov di,word ptr indosflag
mov al,byte ptr es:[di]
or al,al ;INDOS is set ...
jz ok_ret
mov es,word ptr doscriterr+2; ... then ...
mov di,word ptr doscriterr
jmp fixit ;exit with not zero
ok_ret:
xor al,al ;set zero flag
fixit:
ret
checkindos endp
;
; ***************************************************************************
; Internal routine to establish addressibility of the data segment
;
dds proc near
push cs
pop ds
ret
dds endp
newint9 proc far
pushf
cli
push ds
push es
push ax
push bx
push cx
push dx
push di
push si
push cs
pop ds
;internal INT9 processing here
getkey_out:
pop si
pop di
pop dx
pop cx
pop bx
pop ax
pop es
pop ds
out_9:
popf
pushf
cli
call dword ptr cs:oldint9
;
; ***************************************************************************
; This is the int return (IRET) dummy pointer
;
intret:
iret
ignore_9:
in al,60h
push ax
pop ax
in al,61h
mov ah,al
or al,80h
out 61h,al
xchg ah,al
out 61h,al
mov al,20h
out 20h,al
pop ax
jmp short out_9
newint9 endp
new_bios_flag db 0
newint16 proc far
start_again:
mov new_bios_flag,0
cmp ah,00 ;if char request,
je func00 ;loop for character
cmp ah,01 ;if character availability test
je func01 ;go check for char
and ah,10h ;test for extra bios
jz gobios16
mov new_bios_flag,10h
and ah,3
jmp short start_again
gobios16:
or ah,new_bios_flag
jmp dword ptr cs:[oldint16]
;go to bios interrupt 16
func01:
push ds
push si
push di
call dds
func01A:
call keystat ;look at key buffer
pushf ;save return flags
jz fret01 ;return if no key
call hotcheck ;test for hot key
jnz fret01 ;not there
popf ;flags back for loop
jmp func01A ;it was a hot key -- skip it!
fret01:
popf ;flags back
pop di
pop si
pop ds
ret 2 ;return to user
func00:
push ds
push si
push di
call dds
func00A:
call keystat ;wait until character available
jz func00A ;loop it
call hotcheck ;test for a hot key
jz func00A ;it sure was -- start loop again
b_func0:
mov ah,0 ;get the next user key
or ah,new_bios_flag
pushf ;simulate it ...
call dword ptr cs:oldint16 ;do bios
func_out:
pushf ;save return flags
jmp fret01 ;restore and return
newint16 endp
;
; call the background task if no key is available
;
keystat proc near
;preserves all registers except
;AX,SI,DI,DS & flags
push cx
push bx
push ax
push ds
cld
mov ax,BIOS_DATA
mov ds,ax ;set data segment
mov si,KB_FLG1 ;set source
lodsw ;get the high and low bytes
pop ds
mov bx,ax ;save it away
pop ax
mov al,SHIFTST ;shift state check
or al,al ;is it zero?
jz sim_16 ;yes -- set flag for simulation
push bx ;save current flags
and bl,al ;mask bits
cmp al,bl ;are we the same?
pop bx
jne skip_16A ;nope -- next one
sim_16:
or word ptr STATUS,SHIFTSON ;set the flag
mov al,HOTKEY ;is this control block the last one?
or al,al
jnz short skip_16
or word ptr STATUS,HOTKEYON ;turn on hotkey flag
jmp short skip_16
skip_16A:
and word ptr STATUS,NOT SHIFTSON ;set the flag
skip_16:
mov ah,1 ;see if character is available
or ah,new_bios_flag
pushf ;simulate interrupt
call dword ptr cs:oldint16
jnz exit_161 ;got one
chkdos_161:
cld
push ds ;check if dos critical error in effect
push si
lds si,cs:doscriterr ;zero says dos is interruptable
lodsb ;$ff says dos is in a critical state
lds si,cs:indosflag ;if indos then int $28 issued by dos
or al,[si] ;so we dont have to.
;account for active interrupts
or al,cs:intflags ;any flags says we dont issue call
pop si ;to the background.
pop ds
cmp al,01 ;must be indos flag only
jg skip28 ;dos cannot take an interrupt yet
int 28h ;call dos idle function (background dispatch).
skip28:
xor ax,ax ;show no keycode available
exit_161:
pop bx
pop cx
ret
keystat endp
hotcheck proc near ;DI points to TSR Block character is from
;SI holds ID of TSR routine
push cx
push ax ;save character
push di
mov al,HOTKEY
or al,al
jz only_shift ;special case for shift-state only
cmp al,ah ;do scan codes match?
jne again_16H
only_shift:
push ax
mov ax,STATUS ;get TSR status flags
test ax,SHIFTSON ;are shift codes set?
jz again_16H1 ;nope -- try next one
or ax,HOTKEYON ;turn on hotkey flag
mov STATUS,ax ;put new flags back
pop ax
or al,al ;test for special case
pop di ;get back Block pointer
pop ax ;throwing this away
jz hot_out ;special case for flags only
b_func1:
mov ah,0 ;get rid of the character
or ah,new_bios_flag
pushf ;simulate interrupt
call dword ptr cs:oldint16 ;do bios
hot_out:
xor ax,ax ;set zero flag
jmp got_hot
again_16H1:
pop ax ;get rid of flag
again_16H:
mov al,0ffh
or al,al ;clear zero flag if set
pop di
pop ax ;get back keystroke
got_hot:
pop cx ;get back cx register
ret
hotcheck endp
newint8 proc far
pushf
push ds
push di
push cx ;save regs
push ax
call dds
mov ax,STATUS
test ax,HOTKEYON ;is hot-key flag set?
jz again_8_2
test ax,TSRACTIVE ;are we already running?
jnz again_8_2 ;yep -- next one
wait_8:
cmp waitcount,0 ;if waiting, check time
jnz wait2_8 ;decrement count
chkdos_8:
cld
push ds ;check if dos critical error in effect
push si
lds si,cs:doscriterr ;zero says dos is interruptable
lodsb ;$ff says dos is in a critical state
lds si,cs:indosflag ;add in second status byte
or al,[si]
or al,cs:intflags ;any flags says we're busy
pop si
pop ds
jnz wait1_8 ;we're busy -- set count & EXIT
call dopopup ;call the popup stuff
jmp end_8 ;and out we go
wait1_8:
mov waitcount,10h ;set the count
wait2_8:
dec waitcount
jz chkdos_8
again_8_2:
end_8:
pushf ;simulate interrupt ...
call dword ptr oldint8 ;do the original
pop ax ;get back registers
pop cx
pop di
pop ds
popf
exit_8:
iret
newint8 endp
newint28 proc far
pushf ;simulate interrupt ...
call dword ptr cs:oldint28 ;do the original
push ds
push di
push cx ;save regs
push ax
push cs
pop ds
again_28:
mov ax,STATUS ;get TSR status flags
test ax,HOTKEYON ;is hot-key flag set?
jz again_28_2
test ax,TSRACTIVE ;are we already running?
jnz again_28_2 ;yep -- next one
chkdos_28:
cld
push ds ;check if dos critical error in effect
push si
lds si,cs:doscriterr ;zero says dos is interruptable
lodsb ;$ff says dos is in a critical state
or al,cs:intflags ;any flags says we're busy
;NOTE: Indos flag is always set here!
; ... so don't have to check it!
pop si
pop ds
jnz end_28 ;we're busy-EXIT & try again next time
mov waitcount,0 ;clear waiting count and ...
call dopopup ;... call the popup stuff
jmp end_28 ;and out we go
again_28_2:
end_28:
pop ax ;get back registers
pop cx
pop di
pop ds
exit_28:
iret
newint28 endp
di28 dw 0
ds28 dw 0
dopopup proc near
cli ;no interrupts now!!!
or word ptr STATUS,TSRACTIVE ;set active bit
mov ISSEG,ss ;save user's stack segment
mov ISPTR,sp ;... and stack pointer
;DS was saved in INT8 or INT28
mov ss,SSEG ;load TSR stack segment ...
mov sp,SPTR ;... and stack pointer
push bp ;save all registers except for
push bx ;...those saved by calling routine,
push dx ;...DS,DI,CX,AX are restored on exit
push si ;...from this procedure
push es
mov [di28],di ;save TSR pointer until after saving
mov [ds28],ds ;...Indos stack
mov di,sp ;destination is our stack
dec di
dec di ;back off current word
mov ax,ss ;move stack segment
mov es,ax ;...to extra segment
mov si,cs:dossptr ;source is DOS Indos Primary Stack
mov ds,cs:dossseg ;...and segment, of course
dec si ;point to last word on DOS stack
dec si
mov cx,40h ;stack size to save
mov ax,sp ;save current stack pointer ...
sub ax,cx ;...and make room to avoid
sub ax,cx ;...overwriting during REP
mov sp,ax ;...and put it back
std ;set the direction flag ...
rep movsw ;and move the 40 word stack
mov sp,di
cld ;direction flag back
mov di,cs:di28 ;get TSR pointer back
mov ds,cs:ds28
sti ;interrupts back on!!
getdta OLDDTA ;save current DTA
call getpsp ;get current PSP segment
mov OLDPSP,bx ;...returned in BX
mov bx,PSP ;set TSR's PSP in BX
call setpsp ;...and do it
setdta DTA
xor ax,ax
call setup_int ;replace with our handler
push ds ;save DS:DI for after popup
push di
mov ds,DSEG ;load user's data segment
call dword ptr cs:POPUP ;call the user routine!!
pop di ;restore ds:di for addressibility
pop ds
mov bx,OLDPSP ;replace interrupted PSP
call setpsp
setdta OLDDTA ;restore interrupted DTA
xor ax,ax
call restore_int ;restore interrupts as well
;terminate checking here!
cli ;no interrupts while manipulating stack
mov cx,40h ;stack size here
mov di28,di ;save TSR pointer until after saving
mov ds28,ds ;...Indos stack
mov ax,ss ;source is current stack
mov ds,ax
mov si,sp ;point to last word on our stack
inc si
inc si
mov es,cs:dossseg ;destination is dos stack
mov di,cs:dossptr
sub di,cx ;point backward to last used word
sub di,cx ;...on dos stack
cld
rep movsw
mov sp,si ;skip over moved words
mov di,cs:[di28] ;get TSR pointer back
mov ds,cs:[ds28]
pop es
pop si
pop dx
pop bx
pop bp
;
; ***************************************************************************
;
; CAUTION!!!! THE USER ROUTINE MUST RETURN SS:SP WITH THE SAME
; VALUES AS ON ENTRY. OTHER REGISTERS WILL BE RESTORED
; BY THIS ROUTINE.
;
; ***************************************************************************
;
back_popup:
mov ss,ISSEG ;restore user's stack segment
mov sp,ISPTR ;... and stack pointer
;DS will be restored in INT8 or INT28
and STATUS,NOT HOTKEYON+TSRACTIVE
;clear hot-key and inuse bits
sti ;enable interrupts again
dopopup_end:
ret
dopopup endp
newint13 proc far
pushf
or cs:intflags,ININT13
popf ;get back original flags
pushf ;simulate interrupt ...
call dword ptr cs:oldint13 ;do the original
pushf
and cs:intflags,NOT ININT13
popf
ret 2
newint13 endp
newint21 proc far
pushf
sti ;allow interrupts
cmp ah,63h ;max number
jg notme
push ax ;some INT21 functions must be left
push bx ;...alone. They either never return,
mov bx,offset dos21funcs ;...grab parameters from the stack
mov al,ah ;...or can be interrupted.
xlat cs:dos21funcs ;test function against table
or al,al ;wait for functions marked zero
pop bx ;these ,ust be left alone
pop ax
jz notme ;jump to original INT21
jmp short set_21
notme:
popf
jmp dword ptr cs:oldint21
set_21:
or cs:intflags,ININT21 ;say int21 is active
popf ;get original flags back
pushf ;save flags and all regs that
call dword ptr cs:oldint21 ;do the interrupt
sti
pushf ;save return registers
and cs:intflags,NOT ININT21 ;not active any more
popf ;they're back
ret 2 ;return with flags set
newint21 endp
error24 dw 0
newint24 proc far
pushf
mov cs:error24,di ;save the error
mov al,0
iret
newint24 endp
pop_routine proc far ;just prints a msg and gets out
lea dx,hello
mov ah,9
int 21h ; display copyright notice
ret
pop_routine endp
TSR_stack db 512 dup(0)
TSR_stack_end:
res_part: ; this is the end of my resident code so I can tell DOS
; how big I am
;
;
; ***************************************************************************
;
; Initialization Code -- load routine and point new int14
;
second_time db 0 ;flag used to test for odd-byte check
begin:
lea dx,cpyrt
mov ah,9
int 21h ; display copyright notice
call getpsp ;Current PSP returned in BX
mov PSP,bx
getdta DTA ;macro to get Disk Transfer Address
mov ax,ds
mov DSEG,ax ;save it
mov SSEG,ax ;save it here too!
mov word ptr POPUP+2,ax ;save it here three!
mov ax,offset TSR_stack_end
sub ax,2
mov SPTR,ax
mov SHIFTST,08h ;ALT-Key + ...
mov HOTKEY,2dh ;...'X' to pop up
mov word ptr POPUP,offset pop_routine
mov ax,3400h ;find the DOS Indos Byte
int 21h
crit2:
mov word ptr indosflag,bx ;offset returned in BX
mov word ptr indosflag+2,es ;segment returned in ES
mov dossseg,es ;this is also the DOS stack segment
mov word ptr doscriterr+2,es;as well as the segment for the critical flag
mov cx,2000h ;search for instruction: CMP [crit flag],00
mov ax,3e80h ;3e80 is opcode to search for
mov di,bx ;start search here
crit3:
repnz scasw ;search until a match is found
jnz no_crit_found ;couldn't find critical byte
;we're going to search for the
;address of the critical flag
;es:[di-2] now points to:
; CMP [crit flag],00
; JNZ ...
; MOV SP,indos stack address
mov al,byte ptr es:[di+5] ;MOV SP,xxxx
cmp al,0bch ;opcode for MOV SP
jne crit3 ;bad place. Sorry
mov ax,word ptr es:[di] ;here's the critical byte address
mov word ptr doscriterr,ax
mov ax,word ptr es:[di+6] ;...and here's the stack pointer
mov word ptr dossptr,ax
mov ax,1
jmp no_error
no_crit_found:
mov al,second_time ;did we already try odd bytes?
or al,al
jz crit_again ;nope ...
jmp bad_dos_version ;we tried!
crit_again:
mov second_time,1 ;set the flag
inc bx ;increment pointer (needed for AT&T
jmp crit2 ;DOS 2.11 and some others
no_error:
mov ax,1
call setup_int
lea dx,res_part ; load offset of top of code
add dx,15 ;round up
shr dx,1
shr dx,1
shr dx,1
shr dx,1 ; divide by 16 to get paragraphs
xor al,al ; no errors
mov ah,31h ; load terminate & stay resident code
int 21h
bad_dos_version:
lea dx,bad_dos_msg
mov ah,9
int 21h ; display error message
mov ax,4cffh ;exit with -1 return code
int 21h
code ends
end start